#include "internal.h"
#include "Defines.h"
#include "DeviceProperty.h"
#include "PortableDeviceProps.h"
#include "ObjectLock.h"
#include "SensorDdi.h"
#include "Sensor.h"
#include "SensorRegistry.h"
#include "Client.h"
#include "DataQueue.h"
#include "Thread.h"
#include "ClientsManager.h"
#include "Sensor.tmh"

CSensor::CSensor(void)
{
	m_bInitialized = false;
	m_State = SENSOR_STATE_NOT_AVAILABLE;
}

CSensor::~CSensor(void)
{
}

//
// Defaut implementation of "Init".  All sub-classes MUST
// ensure that this implementation of "Init" is called when
// they override it.  Sub-classes are expected to provide
// thread synchronization safe-guards to avoid dead-locks where
// this class and the sub-class locks on the same objects.
//
HRESULT CSensor::Init(IWDFDevice *pDevice, CSensorDDI *pSensorDDI)
{
	//
	// validate input
	//
	if(pDevice == NULL || pSensorDDI == NULL)
		return E_POINTER;

	//
	// set sensor state to "initializing"
	//
	SetState(SENSOR_STATE_INITIALIZING);

	//
	// save device object
	//
	m_spDevice = pDevice;

	//
	// save the sensor DDI object
	//
	m_pSensorDDI = pSensorDDI;

	//
	// initialize all common properties
	//
	HRESULT hr;
	hr = m_Properties.AddProps(DEVPROPS(
		MAKEPROP(WPD_OBJECT_ID, GetId()),
		MAKEPROP(WPD_OBJECT_NAME, GetName()),
		MAKEPROP(WPD_OBJECT_PERSISTENT_UNIQUE_ID, GetId()),
		MAKEPROP(WPD_OBJECT_PARENT_ID, GetParentId()),
		MAKEPROP(WPD_OBJECT_FORMAT, GetFormat()),
		MAKEPROP(WPD_OBJECT_CONTENT_TYPE, GetContentType()),
		MAKEPROP(WPD_OBJECT_CAN_DELETE, (BOOL)(GetCanDelete() ? TRUE : FALSE)),
		MAKEPROP(WPD_FUNCTIONAL_OBJECT_CATEGORY, GetFunctionalObjectCategory())
	));

	return hr;
}

//
// Sets the current state.  Returns the old state.
//
SensorState CSensor::SetState(SensorState state)
{
	CObjectLock lock(this);

	//
	// save the state
	//
	SensorState old = m_State;
	m_State = state;

	//
	// update state property
	//
	m_Properties.SetValue(SENSOR_PROPERTY_STATE, (ULONG)m_State);

	//
	// notify state change
	//
	OnStateChange(old, m_State);
	return old;
}

void CSensor::OnStateChange(SensorState oldState, SensorState newState)
{
	//
	// TODO:
	//	Notify event manager of state change.
	//
	UNREFERENCED_PARAMETER(oldState);

	//
	// raise the state change event
	//
	CClientsManager::GetClientsManager().PostStateChange(GetId(), newState);
}

//
// Default implementation of common sensor properties.
//
wstring CSensor::GetParentId() const
{
	return WPD_DEVICE_OBJECT_ID;
}

GUID CSensor::GetFormat() const
{
	return WPD_OBJECT_FORMAT_UNSPECIFIED;
}

GUID CSensor::GetContentType() const
{
	return WPD_CONTENT_TYPE_FUNCTIONAL_OBJECT;
}

bool CSensor::GetCanDelete() const
{
	//
	// cannot delete device by default
	//
	return false;
}

GUID CSensor::GetPUID() const
{
	HRESULT hr;

	//
    // The persistent unique id of this sensor
    //
    GUID idGuid = GUID_NULL;

	//
	// create a property store for this device if there isn't
	// one already
	//
	CComPtr<IWDFNamedPropertyStore> spPropStore;
    hr = m_spDevice->RetrieveDevicePropertyStore(NULL, WdfPropertyStoreCreateIfMissing, &spPropStore, NULL);
    if (SUCCEEDED(hr))
    {
		//
		// if we have already stored the PUID in the store before then
		// we simply load it
		//
        PROPVARIANT vID;
		hr = spPropStore->GetNamedValue(GetId().c_str(), &vID);
        if (SUCCEEDED(hr))
        {
            hr = ::CLSIDFromString(vID.bstrVal, &idGuid);
        }
        else
        {
			//
			// nope, no stored PUID; so let's create a new one
			//
            if (SUCCEEDED(::CoCreateGuid(&idGuid)))
            {
                LPOLESTR lpszGUID = NULL;
                hr = ::StringFromCLSID(idGuid, &lpszGUID);
                if (SUCCEEDED(hr))
                {
                    PropVariantInit(&vID);
                    vID.vt = VT_LPWSTR;
                    vID.pwszVal = lpszGUID;
                    hr = spPropStore->SetNamedValue(GetId().c_str(), &vID);
                    PropVariantClear(&vID);
                }
            }
        }
    }

	return idGuid;
}

HRESULT CSensor::GetSupportedKeys(const CPortableDeviceProps& props,
								  IPortableDeviceKeyCollection **ppKeys) const
{
	//
	// validate input
	//
	if(ppKeys == NULL)
		return E_POINTER;

	HRESULT									hr;
	CComPtr<IPortableDeviceKeyCollection>	spKeys;

	//
	// create the key collection object
	//
	hr = spKeys.CoCreateInstance(CLSID_PortableDeviceKeyCollection);
	if(FAILED(hr))
		return hr;

	//
	// copy the keys
	//
	hr = CopyKeys(props.m_pKeys, spKeys);
	if(SUCCEEDED(hr))
		*ppKeys = spKeys.Detach();

	return hr;
}

HRESULT CSensor::CopyKeys(IPortableDeviceKeyCollection *pFrom,
						  IPortableDeviceKeyCollection *pTo) const
{
	//
	// validate input
	//
	if(pFrom == NULL || pTo == NULL)
		return E_POINTER;

	HRESULT									hr;
	DWORD									dwCount;
	PROPERTYKEY								key;

	//
	// add all the supported properties
	//
	hr = pFrom->GetCount(&dwCount);
	if(FAILED(hr))
		return hr;

	//
	// copy the keys
	//
	for(DWORD index = 0 ; index < dwCount ; ++index)
	{
		hr = pFrom->GetAt(index, &key);
		if(SUCCEEDED(hr))
			pTo->Add(key);
	}

	return S_OK;
}

HRESULT CSensor::GetSupportedProperties(IPortableDeviceKeyCollection **ppKeys) const
{
	//
	// validate input
	//
	if(ppKeys == NULL)
		return E_POINTER;

	CObjectLock	lock(this);
	return GetSupportedKeys(m_Properties, ppKeys);
}

HRESULT CSensor::GetSupportedDataFields(IPortableDeviceKeyCollection **ppKeys) const
{
	//
	// validate input
	//
	if(ppKeys == NULL)
		return E_POINTER;

	CObjectLock	lock(this);
	return GetSupportedKeys(m_Data, ppKeys);
}

HRESULT CSensor::GetDeviceValues(const CPortableDeviceProps& deviceProps,
								 IPortableDeviceKeyCollection *pKeys,
								 IPortableDeviceValues **ppValues) const
{
	//
	// validate input
	//
	if(pKeys == NULL || ppValues == NULL)
		return E_POINTER;

	CPortableDeviceProps	props;
	HRESULT					hr;
	DWORD					dwCount;
	PROPERTYKEY				key;

	hr = pKeys->GetCount(&dwCount);
	if(FAILED(hr))
		return hr;

	//
	// iterate through the data fields needed and populate
	// "props"
	//
	for(DWORD index = 0 ; index < dwCount ; ++index)
	{
		hr = pKeys->GetAt(index, &key);
		if(SUCCEEDED(hr))
		{
			//
			// set the default value for this key to "not found"
			//
			hr = props.SetError(key, HRESULT_FROM_WIN32(ERROR_NOT_FOUND));
			if(SUCCEEDED(hr))
			{
				//
				// get the value from data fields collection on the sensor
				// and initialize "props"
				//
				PROPVARIANT pvar;
				PropVariantInit(&pvar);

				hr = deviceProps.m_pValues->GetValue(key, &pvar);
				if(SUCCEEDED(hr))
					props.SetValue(key, pvar);
			}
		}
	}

	//
	// hand out the values collection
	//
	*ppValues = props.m_pValues.Detach();
	return hr;
}

HRESULT CSensor::GetDataFields(IPortableDeviceKeyCollection *pDataFields,
							   IPortableDeviceValues **ppDataValues) const
{
	//
	// validate input
	//
	if(pDataFields == NULL || ppDataValues == NULL)
		return E_POINTER;

	CObjectLock lock(this);
	return GetDeviceValues(m_Data, pDataFields, ppDataValues);
}

HRESULT CSensor::GetProperties(IPortableDeviceKeyCollection *pPropFields,
							   IPortableDeviceValues **ppPropValues) const
{
	//
	// validate input
	//
	if(pPropFields == NULL || ppPropValues == NULL)
		return E_POINTER;

	CObjectLock lock(this);
	return GetDeviceValues(m_Properties, pPropFields, ppPropValues);
}

HRESULT CSensor::StartIO(IWDFDevice*)
{
	return S_OK;
}

HRESULT CSensor::StopIO(IWDFDevice*)
{
	return S_OK;
}
